package com.rankeiot.core.excel;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.rankeiot.core.anno.Excel;
import com.rankeiot.core.anno.ExcelTemplate;
import com.rankeiot.core.data.Triple;
import com.rankeiot.core.translator.Translator;
import com.rankeiot.core.util.StringUtil;
import com.rankeiot.core.util.castor.Castor;
import org.sagacity.sqltoy.config.annotation.Translate;
import org.sagacity.sqltoy.utils.BeanUtil;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.text.DecimalFormat;
import java.text.Format;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author desire
 */
public class TypedRowHandler<T> implements Consumer<DataRow> {
    //获取和设置字段值的代理
    public static class ValueHolder {
        Function<DataRow,Object> getter;
        BiConsumer setter;
    }
    int startRow=0;
    private Class<T> type;
   // private Map<Integer, BiConsumer> fieldValueSetters = new HashMap<>();
    private List<ValueHolder> fieldValueHolders = new ArrayList<>();
    private Consumer<T> objHandler;
    Map<String, Translator> translators;
    public TypedRowHandler(Class<T> type, Map<String, Translator> translators,Consumer<T> objHandler){
        this.type=type;
        if(translators==null){
            this.translators=new HashMap<>();
        }else{
            this.translators=translators;
        }
        this.objHandler=objHandler;
        ExcelTemplate et = type.getDeclaredAnnotation(ExcelTemplate.class);
        if(et!=null){
            startRow =et.startRow();
            initFieldValueHolders(null);
            if(fieldValueHolders.size()==0){
                startRow--;
            }
        }
    }
    @Override
    public void accept(DataRow row) {
        int rowIndex=row.getRowNum();
        if(rowIndex<startRow){
            return;
        }else if(rowIndex==startRow&& fieldValueHolders.size()==0){
            initFieldValueHolders(row);
        }else{
            Object obj = null;
            try {
                obj = type.getConstructor().newInstance();
                for(ValueHolder vc: fieldValueHolders){
                    Object c=vc.getter.apply(row);
                    if(c==null){
                        vc.setter.accept(obj, null);
                    }else{
                        vc.setter.accept(obj, c);
                    }
                }
                objHandler.accept((T)obj);
            } catch (InstantiationException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static int excelColumnToNumber(String column) {
        char[] chars = column.toUpperCase().toCharArray();
        int result = 0;
        for(char c:chars){
            result = result * 26 + (c - 'A' + 1);
        }
        return result-1;
    }

    /**
     * 初始化字段设置器
     * @param row
     */
    private void initFieldValueHolders(DataRow row){
        Map<String, Triple<Excel, Field, Integer>> imports = new HashMap<>();
        Set<String> fields = new HashSet<>();
        for (Field field : type.getDeclaredFields()) {
            Excel importField = field.getAnnotation(Excel.class);
            if (importField == null) {
                continue;
            }
            fields.add(field.getName());
            if (!importField.in()) {
                continue;
            }
            String name = importField.value();
            if (StringUtil.isEmpty(name)) {
                name = field.getName();
            }
            int colNum=-1;
            if(StringUtil.isNotEmpty(importField.columnName())){
                colNum=excelColumnToNumber(importField.columnName());
            }else{
                colNum = importField.column();
            }
            if(colNum>-1){
                imports.put(name, new Triple<>(importField, field,colNum));
            }else{
                imports.put(name, new Triple<>(importField, field));
            }

        }
        for (Field field : type.getSuperclass().getDeclaredFields()) {
            Excel importField = field.getAnnotation(Excel.class);
            if (importField == null) {
                continue;
            }
            if (fields.contains(field.getName())) {
                continue;
            }
            if (!importField.in()) {
                continue;
            }

            String name = importField.value();
            if (StringUtil.isEmpty(name)) {
                name = field.getName();
            }
            int colNum=-1;
            if(StringUtil.isNotEmpty(importField.columnName())){
                colNum=excelColumnToNumber(importField.columnName());
            }else{
                colNum = importField.column();
            }
            if(colNum>-1){
                imports.put(name, new Triple<>(importField, field,colNum));
            }else{
                imports.put(name, new Triple<>(importField, field));
            }
        }
        if (imports.isEmpty()) {
            throw new RuntimeException(type.getName() + "没有任何字段被@Excel标注");
        }
        if(row!=null){
            int lc=row.size();
            for (int col = 0; col < lc; col++) {
                Object cell = row.get(col);
                if (cell == null) {
                    continue;
                }
                String cellName = cell.toString();
                if (StringUtil.isEmpty(cellName)) {
                    continue;
                }
                Triple<Excel, Field, Integer> def = imports.get(cellName);
                if (def != null&&def.getThree()==null) {
                    def.setThree(col);
                }
            }
        }
        for (Triple<Excel, Field, Integer> def : imports.values()) {
            Integer i = def.getThree();
            if (i == null) {
                continue;
            }
            Field field = def.getTow();
            Excel ei = def.getOne();
            Translator translator = translators.get(def.getTow().getName());
            if (translator == null) {
                Translate translate = field.getAnnotation(Translate.class);
                if (translate == null) {
                    translator = Translator.DEFAULT;
                } else {
                    //获得反向翻译
                    Translator inner = ExcelUtil.getUnTranslator(translate.cacheName(), translate.cacheType(), translate.join());
                    translator = (target, filedName, value) -> {
                        Object t1 = inner.translate(target, filedName, value);
                        try {
                            BeanUtil.setProperty(target, translate.keyField(), t1);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        return value;
                    };
                }
            }
            Translator finalTranslator = translator;
            final boolean isString = field.getType() == String.class;
            ValueHolder vc=new ValueHolder();
            if(ei.link()){
                vc.getter=(data)->data.getLink(def.getThree());
            }else{
                vc.getter=(data)->data.get(def.getThree());
            }
            if (isString) {
                JsonFormat jf = field.getAnnotation(JsonFormat.class);
                String numFormat = "#.######";
                if (jf != null && StringUtil.isNotEmpty(jf.pattern())) {
                    numFormat = jf.pattern();
                }
                final Format simpleFormat = new DecimalFormat(numFormat);
                BiConsumer cs = (obj, value) -> {
                    Object realValue = finalTranslator.translate(obj, field.getName(), value);
                    try {
                        if (realValue instanceof Number) {
                            realValue = simpleFormat.format(realValue);
                        }
                        BeanUtil.setProperty(obj, field.getName(), realValue);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                };
                //fieldValueSetters.put(i, cs);
                vc.setter=cs;
            } else{
                Castor castor = Castor.getCastor(field.getType());
                BiConsumer cs = (obj, value) -> {
                    Object realValue = finalTranslator.translate(obj, field.getName(), value);
                    if(castor!=null){
                        realValue=castor.cast(realValue);
                    }
                    try {
                        BeanUtil.setProperty(obj, field.getName(), realValue);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                };
                //fieldValueSetters.put(i, cs);
                vc.setter=cs;
            }
            fieldValueHolders.add(vc);

        }
    }

}
